package com.css.fxfzdzzh.util.fileParsing.shp;

import cn.hutool.core.date.DateUtil;
import com.css.fxfzdzzh.base.exception.ShpFieldException;
import com.css.fxfzdzzh.constants.GeomTypeEnum;
import com.css.fxfzdzzh.util.fileParsing.ParsingResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.Filter;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: zhangSongRui
 * @CreateTime: 2022/12/27
 * @Description: shp文件解析
 */
@Slf4j
public class ShpAnalyticUtil {


    /**
     * 解析shp文件
     *
     * @param prj .prj文件的真实地址
     * @param cpg .cpg文件的真实地址
     * @param shp .shp文件的真实地址
     * @return
     */
    public static ParsingResult analytic(String prj, String cpg, String shp) {
        log.info("进行shp文件解析 >> {}", shp);
        //校验是否为2000坐标系
        if (!(checkCGCS2000(prj))) {
            log.info(new Date() + "-----------校验失败.请上传正确的CGCS2000坐标系数据及.prj文件.");
            return ParsingResult.shpData("请上传正确的CGCS2000坐标系数据及.prj文件", null);
        }
        //读取cpg文件编码
        String codeType = readCPG(cpg);
        //获取shp数据
        SimpleFeatureCollection colls1 = readShp(shp, null, codeType);
        SimpleFeatureIterator iters = colls1.features();
        ArrayList<List<Object>> lists = new ArrayList<>();
        try{
            while (iters.hasNext()) {
                SimpleFeature next = iters.next();
                List<Object> attributes = next.getAttributes();
                lists.add(attributes);
            }
        }catch (IllegalArgumentException e){
            return ParsingResult.shpData("数据中存在不合法的参数", null);
        }

        if (lists.size() == 0) {
            return ParsingResult.shpData("解析失败shp文件中没有数据", lists);
        }
        iters.close();
        log.info("shp文件解析成功 >> {}", shp);
        return ParsingResult.shpData("ok", lists);
    }

    //校验是否为2000坐标系
    public static boolean checkCGCS2000(String prj) {
        String strWKTMercator = readPRJ(prj);
        if (strWKTMercator == "") return false;
        CoordinateReferenceSystem sourceTarget = null;
        try {
            sourceTarget = CRS.parseWKT(strWKTMercator);
        } catch (FactoryException e) {
            e.printStackTrace();
            log.error(prj + ": 解析错误!");
        }
        String name = sourceTarget.getName().toString();
        /*if (name.indexOf("CGCS2000") != -1 || name.indexOf("cgcs2000") != -1 || name.indexOf("Geodetic_Coordinate_System_2000") != -1 ||
                name.indexOf("Geodetic Coordinate System 2000") != -1) return true;*/
//        String prjStr = 'GEOGCS["GCS_China_Geodetic_Coordinate_System_2000",DATUM["D_China_2000",SPHEROID["CGCS2000",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]';
        //应上传EPSG:4490的CGCS2000坐标数据，防止坐标解析错误
        if(name.indexOf("GCS_China_Geodetic_Coordinate_System_2000") != -1){
            return true;
        }
        return false;
    }

    //读取prj文件内容
    public static String readPRJ(String path) {
        StringBuffer result = new StringBuffer();
        File file = new File(path);
        if (!file.exists()) return "";
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            while ((tempString = reader.readLine()) != null) {
                result.append(tempString);
                log.info(tempString);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    //读取cpg文件编码
    public static String readCPG(String path) {
        String codeType = "UTF-8";
        StringBuffer result = new StringBuffer();
        File file = new File(path);
        if (!file.exists()) return "";
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            while ((tempString = reader.readLine()) != null) {
                result.append(tempString);
                System.out.println(tempString);
            }
            reader.close();
            //返回结果
            codeType = result.toString();
            codeType = codeType.toUpperCase();//转换大写
        } catch (IOException e) {
            e.printStackTrace();
        }
        return codeType;
    }

    public static SimpleFeatureCollection readShp(String path, Filter filter, String codeType) {
        SimpleFeatureSource featureSource = readStoreByShp(path, codeType);
        if (featureSource == null) return null;
        try {
            return filter != null ? featureSource.getFeatures(filter) : featureSource.getFeatures();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static SimpleFeatureSource readStoreByShp(String path, String codeType) {
        File file = new File(path);
        FileDataStore store;
        SimpleFeatureSource featureSource = null;
        try {
            store = FileDataStoreFinder.getDataStore(file);
            ((ShapefileDataStore) store).setCharset(Charset.forName(codeType));
            featureSource = store.getFeatureSource();
            store.dispose();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return featureSource;
    }

    /**
     * 检查数据 - double类型
     *
     * @param text
     * @param msg
     * @param must 是否必填
     * @return
     * @throws ShpFieldException
     */
    public static Double getDoubleVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getDoubleVal4Must(text, msg);
        } else {
            return getDoubleVal(text, msg);
        }
    }

    public static Integer getIntegerVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getIntegerVal4Must(text, msg);
        } else {
            return getIntegerVal(text, msg);
        }
    }

    public static String getStringVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getStringVal4Must(text, msg);
        } else {
            return getStringVal(text);
        }
    }

    public static Date getDateTimeVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getDateTimeVal4Must(text, msg);
        } else {
            return getDateTimeVal(text, msg);
        }
    }

    public static Date getDateVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getDateVal4Must(text, msg);
        } else {
            return getDateVal(text, msg);
        }
    }

    public static Date getTimeVal(Object text, String msg, boolean must) throws ShpFieldException {
        if (must) {
            return getTimeVal4Must(text, msg);
        } else {
            return getTimeVal(text, msg);
        }
    }


    private static Double getDoubleVal(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        try {
            return Double.parseDouble(text.toString());
        } catch (NumberFormatException e) {
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Double getDoubleVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        try {
            return Double.parseDouble(text.toString());
        } catch (NumberFormatException e) {
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Integer getIntegerVal(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        try {
            return Integer.parseInt(text.toString());
        } catch (NumberFormatException e) {
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Integer getIntegerVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        try {
            return Integer.parseInt(text.toString());
        } catch (NumberFormatException e) {
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static String getStringVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        return text.toString();
    }

    private static String getStringVal(Object text) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        return text.toString();
    }

    private static Date getDateTimeVal(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        try {
            return (Date) text;
        } catch (ClassCastException e1) {
            return DateUtil.parseDateTime(text.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Date getDateTimeVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        try {
            return (Date) text;
        } catch (ClassCastException e1) {
            return DateUtil.parseDateTime(text.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Date getDateVal(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        try {
            return (Date) text;
        } catch (ClassCastException e1) {
            return DateUtil.parseDate(text.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Date getDateVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        try {
            return (Date) text;
        } catch (ClassCastException e1) {
            try {
                return DateUtil.parseDate(text.toString());
            } catch (Exception e2) {
                log.error(e2.getMessage(), "错误数据 > " + text);
                throw new ShpFieldException(msg + "数据类型有误");
            }
        } catch (Exception e3) {
            log.error(e3.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Date getTimeVal(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            return null;
        }
        try {
            return DateUtil.parseTime(text.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }

    private static Date getTimeVal4Must(Object text, String msg) throws ShpFieldException {
        if (text == null || StringUtils.isBlank(text.toString())) {
            throw new ShpFieldException(msg + "不能为空");
        }
        try {
            return DateUtil.parseTime(text.toString());
        } catch (Exception e) {
            log.error(e.getMessage(), "错误数据 > " + text);
            throw new ShpFieldException(msg + "数据类型有误");
        }
    }
    public static String checkGeomTypeOf(String val, GeomTypeEnum... geomTypes) throws ShpFieldException {
        boolean flag = false;
        if(StringUtils.isNotBlank(val)){
            for (GeomTypeEnum geomType : geomTypes) {
                if (val.contains(geomType.name())) {
                    flag = true;
                }
            }
        }
        if (!flag) {
            throw new ShpFieldException("空间数据类有误，请上传" + StringUtils.join(Arrays.stream(geomTypes).map(GeomTypeEnum::getDescByEnum).collect(Collectors.toList()), "或") + "类型的空间数据");
        }
        return val;
    }
}
